
from flask import Flask, render_template, request, jsonify, session, redirect, url_for
import os
import subprocess
import datetime
import configparser
import threading
import pystray
import logging
import sys
import time
import wave
import io
import hashlib
from PIL import Image
import webbrowser
import signal
from flask import Flask, render_template, request, jsonify
from flask_cors import CORS

#导入更改进程名相关库
# import os
# from setproctitle import setproctitle

# 初始化Flask应用
# 初始化Flask应用
app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
app.secret_key = 'a_secure_secret_key_12345'  # 更安全的会话密钥

# 全局播放控制变量
is_bell_playing = False
bell_player_process = None
bell_player_thread = None
bell_player_stop_event = None

# 允许显示Windows控制台窗口

# 配置标准输出编码
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')

# 配置日志
def setup_logging():
    """配置日志，创建当前日期的日志文件"""
    logger = logging.getLogger('AppLogger')
    logger.setLevel(logging.INFO)

    # 使用脚本所在目录作为基准，确保日志目录创建在正确位置
    script_dir = os.path.dirname(os.path.abspath(__file__))
    base_log_dir = os.path.join(script_dir, 'AppLog')
    now = datetime.datetime.now()
    info_date_dir = os.path.join(base_log_dir, 'INFO', now.strftime('%Y'), now.strftime('%m'), now.strftime('%d'))
    
    # 确保目录存在
    os.makedirs(info_date_dir, exist_ok=True)
    
    # 验证目录创建成功
    if not os.path.exists(info_date_dir):
        # 如果目录不存在，尝试强制创建
        try:
            os.makedirs(info_date_dir, mode=0o755, exist_ok=True)
        except Exception as e:
            # 作为最后手段，使用当前工作目录
            base_log_dir = 'AppLog'
            info_date_dir = os.path.join(base_log_dir, 'INFO', now.strftime('%Y'), now.strftime('%m'), now.strftime('%d'))
            os.makedirs(info_date_dir, exist_ok=True)

    # 生成时间戳，格式为：年月日_时-分-秒
    timestamp = now.strftime('%Y%m%d_%H-%M-%S')
    info_log = os.path.join(info_date_dir, f'app_{timestamp}.log')

    # 创建文件处理器
    info_handler = logging.FileHandler(info_log, encoding='utf-8')
    info_handler.setLevel(logging.INFO)

    # 设置日志格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    info_handler.setFormatter(formatter)

    # 清除已有的处理器
    if logger.handlers:
        for handler in logger.handlers[:]:
            handler.close()
            logger.removeHandler(handler)

    # 添加处理器到logger
    logger.addHandler(info_handler)

    return logger, info_log

def reset_logging():
    """每天0:00自动重新配置日志，创建新一天的日志文件"""
    try:
        old_logger = logging.getLogger('AppLogger')
        old_log_path = None
        
        # 获取旧日志文件路径
        if old_logger.handlers:
            for handler in old_logger.handlers:
                if isinstance(handler, logging.FileHandler):
                    old_log_path = handler.baseFilename
                    break
        
        # 创建新日志
        new_logger, new_log_path = setup_logging()
        
        # 记录日志重置过程
        new_logger.info("=" * 60)
        new_logger.info(f"新一天日志文件创建开始: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        if old_log_path and os.path.exists(old_log_path):
            new_logger.info(f"旧日志文件: {old_log_path}")
        new_logger.info(f"新日志文件已创建: {new_log_path}")
        new_logger.info("新一天日志文件创建完成")
        new_logger.info("=" * 60)
        
        return new_logger
    except Exception as e:
        # 如果重配置失败，使用原logger记录错误
        old_logger = logging.getLogger('AppLogger')
        if old_logger.handlers:
            old_logger.error(f"重新配置日志失败: {e}")
        return old_logger

# 初始化日志
logger, log_file_path = setup_logging()

# 验证日志文件是否创建成功
if not os.path.exists(log_file_path):
    # 如果日志文件不存在，尝试重新创建
    try:
        # 强制创建目录和文件
        log_dir = os.path.dirname(log_file_path)
        os.makedirs(log_dir, exist_ok=True)
        with open(log_file_path, 'w', encoding='utf-8') as f:
            f.write(f"强制创建的日志文件 - {datetime.datetime.now()}\n")
        print(f"警告：强制创建日志文件 {log_file_path}")
    except Exception as e:
        print(f"错误：无法创建日志文件 {log_file_path}: {e}")

print(f"日志文件路径: {log_file_path}")

# 全局变量用于日志重置线程
log_reset_thread = None
log_reset_running = False

def log_reset_worker():
    """后台线程：每天0:00自动重新配置日志，创建新一天的日志文件"""
    global logger, log_reset_running
    log_reset_running = True
    last_reset_date = datetime.datetime.now().strftime('%Y-%m-%d')
    
    while log_reset_running:
        try:
            now = datetime.datetime.now()
            current_date = now.strftime('%Y-%m-%d')
            
            # 检查是否为新的一天（无论具体时间，只要日期变化就触发）
            if last_reset_date != current_date:
                logger.info("=" * 60)
                logger.info(f"检测到新日期，开始创建新一天日志文件: {current_date}")
                logger.info(f"当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}")
                logger.info("=" * 60)
                
                logger = reset_logging()
                last_reset_date = current_date
                
                # 休眠一段时间避免重复触发
                time.sleep(3600)  # 休眠1小时
            
            # 每分钟检查一次日期变化
            time.sleep(60)
            
        except Exception as e:
            try:
                logger.error(f"日志重置线程出错: {e}")
            except:
                pass  # 如果logger也出错，静默失败
            time.sleep(60)  # 出错后等待1分钟再试

def start_log_reset_thread():
    """启动日志重置后台线程"""
    global log_reset_thread
    if log_reset_thread is None or not log_reset_thread.is_alive():
        log_reset_thread = threading.Thread(target=log_reset_worker, daemon=True)
        log_reset_thread.start()
        logger.info("日志重置后台线程已启动")
        logger.info("每日0:00自动日志重置功能已启用")

# 确保启动时就有当天的日志，并启动重置线程
today = datetime.datetime.now().strftime('%Y-%m-%d')

# 强制验证日志文件创建
if not os.path.exists(log_file_path):
    logger.error(f"启动时验证：日志文件不存在于 {log_file_path}")
    # 尝试重新创建
    try:
        os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
        with open(log_file_path, 'a', encoding='utf-8') as f:
            f.write(f"{datetime.datetime.now()} - 强制验证日志创建\n")
        logger.info(f"强制创建日志文件成功: {log_file_path}")
    except Exception as e:
        logger.error(f"强制创建日志文件失败: {e}")

logger.info(f"应用启动，当前日期: {today}")
logger.info(f"当前日志文件路径: {log_file_path}")

# 启动日志重置线程
start_log_reset_thread()

# 添加手动测试日志重置的功能
@app.route('/admin/test_log_reset', methods=['POST'])
def test_log_reset():
    """手动触发日志重置测试"""
    if not session.get('is_admin'):
        return jsonify({'success': False, 'message': '权限不足'}), 403
    
    try:
        global logger
        logger.info("=" * 60)
        logger.info(f"管理员手动触发日志重置测试: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        logger.info("=" * 60)
        
        logger = reset_logging()
        
        return jsonify({
            'success': True, 
            'message': '日志重置测试完成，请检查新日志文件',
            'new_log_path': logging.getLogger('AppLogger').handlers[0].baseFilename if logging.getLogger('AppLogger').handlers else '未知'
        })
    except Exception as e:
        return jsonify({'success': False, 'message': f'测试失败: {str(e)}'}), 500

# 添加关闭信号处理
def shutdown_handler(signum=None, frame=None):
    """应用关闭时的清理函数"""
    global log_reset_running
    log_reset_running = False
    logger.info("应用正在关闭，停止日志重置线程...")

# 注册信号处理器
import signal
try:
    signal.signal(signal.SIGINT, shutdown_handler)
    signal.signal(signal.SIGTERM, shutdown_handler)
except:
    pass  # 在非Unix系统上可能不支持

# 获取当前登录用户信息辅助函数
def get_current_user():
    try:
        if 'username' in session:
            return session['username']
        return '未登录用户'
    except RuntimeError:
        # 当没有请求上下文时返回默认值
        return '未登录用户'

# 确保voice文件夹存在
voice_dir = os.path.join(os.getcwd(), 'voice')
if not os.path.exists(voice_dir):
    os.makedirs(voice_dir)
    logger.info(f'创建voice文件夹: {voice_dir}')

# 获取配置文件路径 - 改为使用daily_configs目录
CONFIG_DIR = 'daily_configs'

# 读取Python路径配置
config = configparser.ConfigParser()
# 确保使用相对路径读取配置文件
config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'PythonDir.ini')
config.read(config_file_path, encoding='utf-8')
PYTHON_PATH = config.get('DEFAULT', 'PythonDir', fallback='python.exe')

@app.route('/')
def login_page():
    return render_template('login.html')

@app.route('/login', methods=['POST'])
def login():
    data = request.json
    username = data.get('username')
    password = data.get('password')

    # 读取用户配置
    config = configparser.ConfigParser()
    config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'user.ini')
    try:
        config.read(config_file_path, encoding='utf-8')
    except Exception as e:
        logger.error(f'[{get_current_user()}] 读取用户配置文件失败: {e}')
        return jsonify({'success': False, 'message': '服务器内部错误'})

    # 检查用户是否存在
    if not config.has_section('users') or username not in config['users']:
        return jsonify({'success': False, 'message': '用户不存在'})

    # 生成密码哈希（用户名的前8位哈希）
    try:
        hashed_password = hashlib.md5(username.encode()).hexdigest()[:8]
    except Exception as e:
        logger.error(f'[{get_current_user()}] 生成密码哈希失败: {e}')
        return jsonify({'success': False, 'message': '服务器内部错误'})

    # 验证密码
    if password != hashed_password:
        return jsonify({'success': False, 'message': '密码错误'})

    # 检查是否为管理员
    is_admin = username in ['admin', 'root']

    # 存储用户信息到会话
    session['username'] = username
    session['is_admin'] = is_admin
    logger.info(f'[{username}] 登录成功，管理员状态: {is_admin}')

    # 重定向
    if is_admin:
        redirect_url = f'/admin_console?admin={username}'
    else:
        redirect_url = f'/console?user={username}'

    return jsonify({'success': True, 'redirect_url': redirect_url})

@app.route('/console')
def console():
    username = request.args.get('user')
    if not username or 'username' not in session or session['username'] != username:
        return redirect(url_for('login_page'))
    return render_template('console.html')

@app.route('/admin_console')
def admin_console():
    admin = request.args.get('admin')
    if not admin or 'username' not in session or session['username'] != admin or not session['is_admin']:
        return redirect(url_for('login_page'))
    return render_template('admin_console.html')

@app.route('/logout')
def logout():
    username = get_current_user()
    session.clear()
    logger.info(f'[{username}] 退出登录')
    return redirect(url_for('login_page'))

# 新增管理员用户管理API
@app.route('/admin/get_users', methods=['GET'])
def get_users():
    if not session.get('is_admin'):
        return jsonify({'error': '权限不足'}), 403
        
    config = configparser.ConfigParser()
    config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'user.ini')
    config.read(config_file_path, encoding='utf-8')
    
    users = []
    if config.has_section('users'):
        for username in config['users']:
            # 生成密码（与登录逻辑一致）
            hashed_password = hashlib.md5(username.encode()).hexdigest()[:8]
            users.append({
                'username': username,
                'password': hashed_password
            })
    
    return jsonify(users)

@app.route('/admin/add_user', methods=['POST'])
def add_user():
    if not session.get('is_admin'):
        return jsonify({'success': False, 'message': '权限不足'}), 403
        
    data = request.json
    username = data.get('username', '').strip()
    
    if not username:
        return jsonify({'success': False, 'message': '用户名不能为空'})
        
    if username in ['admin', 'root']:
        return jsonify({'success': False, 'message': '禁止创建管理员用户'})
    
    config = configparser.ConfigParser()
    config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'user.ini')
    config.read(config_file_path, encoding='utf-8')
    
    if not config.has_section('users'):
        config.add_section('users')
    
    if username in config['users']:
        return jsonify({'success': False, 'message': '用户已存在'})
    
    # 添加用户（值设为1表示启用）
    config.set('users', username, '1')
    
    # 保存配置
    with open(config_file_path, 'w', encoding='utf-8') as f:
        config.write(f)
    
    # 生成密码
    hashed_password = hashlib.md5(username.encode()).hexdigest()[:8]
    logger.info(f'[{get_current_user()}] 添加新用户: {username}')
    
    return jsonify({
        'success': True,
        'username': username,
        'password': hashed_password
    })

@app.route('/admin/delete_user', methods=['POST'])
def delete_user():
    if not session.get('is_admin'):
        return jsonify({'success': False, 'message': '权限不足'}), 403
        
    data = request.json
    username = data.get('username', '').strip()
    
    if not username:
        return jsonify({'success': False, 'message': '用户名不能为空'})
        
    if username in ['admin', 'root']:
        return jsonify({'success': False, 'message': '禁止删除管理员用户'})
    
    config = configparser.ConfigParser()
    config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'user.ini')
    config.read(config_file_path, encoding='utf-8')
    
    if not config.has_section('users') or username not in config['users']:
        return jsonify({'success': False, 'message': '用户不存在'})
    
    # 删除用户
    config.remove_option('users', username)
    
    # 保存配置
    with open(config_file_path, 'w', encoding='utf-8') as f:
        config.write(f)
    
    logger.info(f'[{get_current_user()}] 删除用户: {username}')
    return jsonify({'success': True, 'message': '用户已删除'})

# 配置文件管理API
@app.route('/admin/get_config_files', methods=['GET'])
def get_config_files():
    if not session.get('is_admin'):
        return jsonify({'success': False, 'message': '权限不足'}), 403
    try:
        config_files = [f for f in os.listdir(CONFIG_DIR) if f.endswith('.ini')]
        return jsonify(config_files)
    except Exception as e:
        logger.error(f'[{get_current_user()}] 获取配置文件列表失败: {e}')
        return jsonify({'success': False, 'message': '获取配置文件列表失败'})

@app.route('/admin/load_config', methods=['GET'])
def load_config():
    if not session.get('is_admin'):
        return jsonify({'success': False, 'message': '权限不足'}), 403
    file = request.args.get('file')
    if not file:
        return jsonify({'success': False, 'message': '请指定配置文件'}), 400
    file_path = os.path.join(CONFIG_DIR, file)
    if not os.path.exists(file_path) or not os.path.isfile(file_path):
        return jsonify({'success': False, 'message': '配置文件不存在'}), 404
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        return jsonify({'success': True, 'content': content})
    except Exception as e:
        logger.error(f'[{get_current_user()}] 加载配置文件失败: {e}')
        return jsonify({'success': False, 'message': '加载配置文件失败'})

@app.route('/admin/save_config', methods=['POST'])
def save_config():
    if not session.get('is_admin'):
        return jsonify({'success': False, 'message': '权限不足'}), 403
    data = request.json
    file = data.get('file')
    content = data.get('content')
    if not file or not content:
        return jsonify({'success': False, 'message': '请提供配置文件和内容'}), 400
    file_path = os.path.join(CONFIG_DIR, file)
    if not os.path.exists(file_path) or not os.path.isfile(file_path):
        return jsonify({'success': False, 'message': '配置文件不存在'}), 404
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(content)
        logger.info(f'[{get_current_user()}] 保存配置文件成功: {file_path}')
        return jsonify({'success': True, 'message': '配置文件保存成功'})
    except Exception as e:
        logger.error(f'[{get_current_user()}] 保存配置文件失败: {e}')
        return jsonify({'success': False, 'message': '保存配置文件失败'})

@app.route('/get_schedules', methods=['GET'])
def get_schedules():
    # 使用固定的英文星期缩写列表，不受系统区域设置影响
    weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    current_day = weekdays[datetime.datetime.now().weekday()]
    config_file = os.path.join(CONFIG_DIR, f"{current_day}.ini")
    
    if not os.path.exists(config_file):
        return jsonify({"error": f"未找到 {config_file} 配置文件"}), 404
    
    schedules = {}
    try:
        with open(config_file, 'r', encoding='utf-8') as f:
            content = f.read().strip()
        
        # 解析INI格式，提取[TIME]节下的时间
        in_time_section = False
        for line in content.split('\n'):
            line = line.strip()
            if line.startswith('[TIME]'):
                in_time_section = True
                continue
            if in_time_section and line:
                # 解析每个时间点对应的MP3文件
                if '=' in line:
                    time_part, mp3_part = line.split('=', 1)
                    time = time_part.strip()
                    mp3_file = mp3_part.strip()
                    schedules[time] = mp3_file
    
    except Exception as e:
        return jsonify({"error": f"加载 {config_file} 时出错: {e}"}), 500
    
    return jsonify(schedules)

@app.route('/get_config_path', methods=['GET'])
def get_config_path():
    # 使用固定的英文星期缩写列表，不受系统区域设置影响
    weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    current_day = weekdays[datetime.datetime.now().weekday()]
    config_file = os.path.join(CONFIG_DIR, f"{current_day}.ini")
    return jsonify({"config_path": config_file})

@app.route('/update_schedule', methods=['POST'])
def update_schedule():
    data = request.json
    time_str = data.get('time')
    mp3_file = data.get('mp3_file')
    
    if not time_str or not mp3_file:
        return jsonify({"error": "缺少时间或音频文件名"}), 400
    
    # 使用固定的英文星期缩写列表，不受系统区域设置影响
    weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    current_day = weekdays[datetime.datetime.now().weekday()]
    config_file = os.path.join(CONFIG_DIR, f"{current_day}.ini")
    
    if not os.path.exists(config_file):
        return jsonify({"error": f"未找到 {config_file} 配置文件"}), 404
    
    try:
        # 读取现有内容
        with open(config_file, 'r', encoding='utf-8') as f:
            lines = f.readlines()
        
        # 查找[TIME]部分并添加新内容
        with open(config_file, 'w', encoding='utf-8') as f:
            time_section_found = False
            for line in lines:
                f.write(line)
                if line.strip().startswith('[TIME]'):
                    time_section_found = True
            
            # 如果未找到[TIME]部分，添加一个
            if not time_section_found:
                f.write('\n[TIME]\n')
            
            # 添加新的时间-铃声映射
            f.write(f"{time_str} = {mp3_file}\n")
        
        logger.info(f'[{get_current_user()}] 更新铃声安排: {time_str} = {mp3_file}')
        return jsonify({"success": "铃声安排已更新"})
    
    except Exception as e:
        logger.error(f'[{get_current_user()}] 更新铃声安排时出错: {e}')
        return jsonify({"error": f"更新铃声安排时出错: {e}"}), 500

@app.route('/get_audio_files', methods=['GET'])
def get_audio_files():
    try:
        # 获取运行目录下的MP3文件
        root_dir = os.getcwd()
        root_mp3 = [f for f in os.listdir(root_dir) if f.lower().endswith('.mp3')]

        # 获取voice文件夹下的音频文件
        voice_dir = os.path.join(root_dir, 'voice')
        voice_files = []
        if os.path.exists(voice_dir):
            voice_files = [f for f in os.listdir(voice_dir) if f.lower().endswith(('.mp3', '.wav'))]

        # 获取music文件夹下的音频文件
        music_dir = os.path.join(root_dir, 'music')
        music_files = []
        if os.path.exists(music_dir):
            music_files = [f for f in os.listdir(music_dir) if f.lower().endswith(('.mp3', '.wav'))]

        return jsonify({
            'root_mp3': sorted(root_mp3),
            'voice_files': sorted(voice_files),
            'music_files': sorted(music_files)
        })
    except Exception as e:
        logger.error(f"[{get_current_user()}] 获取音频文件列表失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/upload_recording', methods=['POST'])
def upload_recording():
    try:
        if 'audio' not in request.files:
            return jsonify({'success': False, 'error': '未找到音频文件'})

        audio_file = request.files['audio']
        if audio_file.filename == '':
            return jsonify({'success': False, 'error': '未选择音频文件'})

        # 获取前端传递的音频参数
        sample_rate = int(request.form.get('sample_rate', 44100))
        channels = int(request.form.get('channels', 1))
        bits_per_sample = int(request.form.get('bits_per_sample', 16))

        username = get_current_user()
        logger.info(f"[{username}] 接收到音频参数: 采样率={sample_rate}, 声道数={channels}, 位深={bits_per_sample}")

        # 生成文件名 (YYYY-MM-dd_HH_MM_ss.wav)
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')
        filename = f"{timestamp}.wav"

        # 保存文件到voice文件夹
        voice_dir = os.path.join(os.getcwd(), 'voice')
        if not os.path.exists(voice_dir):
            os.makedirs(voice_dir)
            logger.info(f'[{username}] 创建voice文件夹: {voice_dir}')

        file_path = os.path.join(voice_dir, filename)

        # 读取上传的音频数据
        audio_data = audio_file.read()

        # 检查是否为有效的WAV文件
        is_valid_wav = False
        try:
            if audio_data.startswith(b'RIFF') and audio_data[8:12] == b'WAVE':
                # 尝试使用wave模块打开文件以验证完整性
                with wave.open(io.BytesIO(audio_data), 'rb') as wf:
                    # 读取一些基本信息来验证
                    wf.getnchannels()
                    wf.getsampwidth()
                    wf.getframerate()
                    is_valid_wav = True
        except Exception as e:
            logger.warning(f"[{username}] 验证WAV文件失败: {e}")

        if is_valid_wav:
            # 直接保存有效WAV文件
            with open(file_path, 'wb') as f:
                f.write(audio_data)
            logger.info(f"[{username}] 有效WAV文件已保存: {file_path}")
        else:
            # 使用前端提供的参数构建WAV文件
            try:
                with wave.open(file_path, 'wb') as wf:
                    wf.setnchannels(channels)
                    wf.setsampwidth(bits_per_sample // 8)
                    wf.setframerate(sample_rate)
                    wf.writeframes(audio_data)
                logger.info(f"[{username}] 使用前端参数构建并保存WAV文件: {file_path}")
            except Exception as e:
                # 如果构建失败，尝试使用默认参数
                logger.warning(f"[{username}] 使用前端参数构建WAV失败: {e}，尝试使用默认参数")
                with wave.open(file_path, 'wb') as wf:
                    wf.setnchannels(1)
                    wf.setsampwidth(2)
                    wf.setframerate(44100)
                    wf.writeframes(audio_data)
                logger.info(f"[{username}] 使用默认参数构建并保存WAV文件: {file_path}")

        return jsonify({'success': True, 'filename': filename})
    except Exception as e:
        logger.error(f"[{get_current_user()}] 上传录音失败: {e}")
        return jsonify({'success': False, 'error': str(e)})

@app.route('/upload_music', methods=['POST'])
def upload_music():
    try:
        if 'music' not in request.files:
            return jsonify({'success': False, 'error': '未找到音乐文件'})

        music_file = request.files['music']
        if music_file.filename == '':
            return jsonify({'success': False, 'error': '未选择音乐文件'})

        # 生成时间戳前缀
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')
        original_filename = music_file.filename
        # 构建新文件名: YYYY-MM-dd_HH_MM_ss--上传文件名
        filename = f"{timestamp}--{original_filename}"

        # 确保music文件夹存在
        music_dir = os.path.join(os.getcwd(), 'music')
        if not os.path.exists(music_dir):
            os.makedirs(music_dir)
            logger.info(f'[{get_current_user()}] 创建music文件夹: {music_dir}')

        # 保存文件到music文件夹
        file_path = os.path.join(music_dir, filename)
        music_file.save(file_path)
        logger.info(f"[{get_current_user()}] 音乐文件已上传并保存: {file_path}")

        return jsonify({'success': True, 'filename': filename})
    except Exception as e:
        logger.error(f"[{get_current_user()}] 上传音乐失败: {e}")
        return jsonify({'success': False, 'error': str(e)})


@app.route('/play_bell', methods=['POST'])
def play_bell():
    global is_bell_playing, bell_player_process, bell_player_thread, bell_player_stop_event
    
    # 如果当前有音频正在播放，先停止它
    if is_bell_playing and bell_player_thread and bell_player_thread.is_alive():
        stop_bell_internal()
    
    data = request.json
    mp3_file = data.get('mp3_file')
    # 将volume转换为整数类型
    volume_percent = int(data.get('volume', 70))  # 默认音量为70%
    
    if not mp3_file:
        return jsonify({"error": "缺少音频文件名"}), 400
    
    current_dir = os.getcwd()
    # 处理相对路径
    if mp3_file.startswith('voice/') or mp3_file.startswith('music/'):
        file_path = os.path.join(current_dir, mp3_file)
        # 转换为单斜杠路径
        file_path = file_path.replace('\\', '/')
    else:
            file_path = os.path.join(current_dir, mp3_file)
            # 转换为单斜杠路径
            file_path = file_path.replace('\\', '/')

    # 使用BellPlayer.py播放音频
    bell_player_path = os.path.join(current_dir, "BellPlayer.py")

    # 检查BellPlayer.py是否存在
    if not os.path.exists(bell_player_path):
        return jsonify({"error": f"未找到BellPlayer.py文件: {bell_player_path}"}), 404

    # 检查音频文件是否存在
    if not os.path.exists(file_path):
        return jsonify({"error": f"未找到音频文件: {mp3_file}"}), 404

    # 构建命令
    cmd = [PYTHON_PATH, bell_player_path, '-f', file_path, '-v', str(volume_percent / 100.0)]

    logger.info(f"[{get_current_user()}] 执行命令: {' '.join(cmd)}")
    
    # 创建停止事件
    bell_player_stop_event = threading.Event()
    
    # 定义播放函数
    def play_audio():
        global is_bell_playing, bell_player_process
        is_bell_playing = True
        
        try:
            # 使用subprocess.Popen启动播放进程，非阻塞式
            bell_player_process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                env=os.environ.copy(),  # 传递完整环境变量
                cwd=os.getcwd()  # 确保在正确的工作目录执行
            )
            # 等待进程完成
            stdout, stderr = bell_player_process.communicate()
            if bell_player_process.returncode != 0:
                logger.error(f"[{get_current_user()}] 播放进程出错: {stderr}")
            else:
                logger.info(f"[{get_current_user()}] 音频播放完成")
                
        except Exception as e:
            error_message = f"播放进程出错: {e}"
            logger.error(f"[{get_current_user()}] {error_message}")
        finally:
            is_bell_playing = False
            bell_player_process = None
    
    # 在线程中启动播放
    bell_player_thread = threading.Thread(target=play_audio)
    bell_player_thread.start()
    
    return jsonify({"success": f"已成功发送指令播放：{mp3_file}（音量增益：{volume_percent}%）"})

def stop_bell_internal():
    """内部停止播放函数"""
    global is_bell_playing, bell_player_process, bell_player_stop_event
    
    # 向BellPlayer.py进程发送SIGTERM信号
    if bell_player_process:
        try:
            bell_player_process.terminate()
            try:
                bell_player_process.wait(timeout=5)  # 等待最多5秒
            except subprocess.TimeoutExpired:
                bell_player_process.kill()
                bell_player_process.wait()
            logger.info(f"[{get_current_user()}] 已停止播放进程")
        except Exception as e:
            logger.error(f"[{get_current_user()}] 停止播放进程时出错: {e}")
    
    # 重置状态
    is_bell_playing = False
    bell_player_process = None
    bell_player_stop_event = None

@app.route('/stop_bell', methods=['POST'])
def stop_bell():
    global is_bell_playing, bell_player_process
    
    # 调用内部停止函数
    stop_bell_internal()
    
    return jsonify({"message": "已发送停止播放指令"})

def create_tray_icon(logger):
    # 创建托盘图标菜单
    def open_webpage(icon, item):
        # 打开管理网页
        webbrowser.open('http://127.0.0.1:8080')
        logger.info(f'[{get_current_user()}] 用户打开了管理网页')

    def exit_app(icon, item):
        # 停止托盘图标
        logger.info(f'[{get_current_user()}] 用户请求退出程序...')
        icon.stop()
        # 终止Flask应用
        os._exit(0)

    # 加载图标
    # 确保使用相对路径加载图标
    icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon', 'web.ico')
    if not os.path.exists(icon_path):
        # 如果找不到图标，创建一个简单的图标
        image = Image.new('RGB', (64, 64), color='red')
        logger.warning(f'[{get_current_user()}] 未找到图标文件: {icon_path}，使用默认图标')
    else:
        try:
            image = Image.open(icon_path)
            logger.info(f'[{get_current_user()}] 加载图标: {icon_path}')
        except Exception as e:
            image = Image.new('RGB', (64, 64), color='red')
            logger.error(f'[{get_current_user()}] 加载图标失败: {e}')

    # 创建菜单
    menu = (
        pystray.MenuItem('打开管理网页', open_webpage),
        pystray.Menu.SEPARATOR,
        pystray.MenuItem('退出管理后台', exit_app),
    )

    # 创建图标
    icon = pystray.Icon('bell_app', image, '铃声管理', menu)

    # 设置左键点击事件
    icon.left_click = open_webpage

    return icon

if __name__ == '__main__':
    # 设置进程名称
    #setproctitle('gbz_app')
    logger.info(f'[{get_current_user()}] 程序启动')

    try:
        # 创建并启动托盘图标
        tray_icon = create_tray_icon(logger)
        logger.info(f'[{get_current_user()}] 创建托盘图标成功')

        # 启动Flask应用在单独的线程中
        def run_flask():
            app.run(debug=False, port=8080, host='0.0.0.0')
            #app.run(debug=False,port=443,host='0.0.0.0',ssl_context=('cert.pem', 'private.pem'))  # 添加SSL证书路径

        flask_thread = threading.Thread(target=run_flask, daemon=True)
        flask_thread.start()
        logger.info(f'[{get_current_user()}] Flask应用启动成功')

        # 运行托盘图标（这会阻塞当前线程）
        tray_icon.run()
        logger.info(f'[{get_current_user()}] 托盘图标已停止')

    except Exception as e:
        logger.error(f'[{get_current_user()}] 程序运行出错: {e}')
        sys.exit(1)
        